﻿Imports System
Imports System.Drawing
Imports System.Windows.Forms
Imports System.ComponentModel
Imports System.Math
Partial Public Class TextBoxEx
#Region "键盘控制"
    ''' <summary>Key事件
    '''************************************************************************************************************
    '''*      KeyDown  KeyPress  KeyUp
    '''*      在KeyDown事件里可以捕获控制系的按键
    '''*      在KeyPress事件里面可以获取来自IME的输入文字
    '''*      如果按键动作没有被KeyDown过滤掉，产生控制动作，则将KeyPress的Char绘制到控件里
    '''************************************************************************************************************
    '''</summary>
    ''' <param name="sender"></param>
    ''' <param name="e"></param>
    ''' <remarks>  
    '''  '*************************************************************************
    '''  '*      VS2003中，从输入法输入的字符只表示一次，VS2003以后，表示两次
    '''  '*      原因据说和两个消息有关，一个是0x286，一个是0x102
    '''  '*      微软应该多解决掉一些奇怪的BUG，再研究华而不实的新功能！！！！
    '''  '*************************************************************************
    ''' </remarks>
    Private Sub TextBoxEx_KeyPress(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyPressEventArgs) Handles Me.KeyPress
        If ExReadOnlyMode Then Exit Sub
        SimeKey(e.KeyChar)
    End Sub
    ''' <summary>
    ''' 通过键盘操作，向文档中追加文本
    ''' </summary>
    ''' <param name="KeyChar"></param>
    ''' <remarks></remarks>
    Sub SimeKey(ByVal KeyChar As Char)
        '如果不是操作系的按键，不是从IME发出的重复信息，则绘制文字
        '如果是Tab的话，也绘制文字
        Dim IsNeedRefresh As Boolean = False
        '过滤掉ESC的输入
        If KeyChar = Chr(27) Then
            Exit Sub
        End If
        If (IsDrawString And CharFormIME = False) Or KeyChar = Chr(9) Then
            '保存这个关于块注释的原始信息
            Dim orgBlockStart As Boolean = ExDocument.GetCurrentHeaderLineInfo.Real_IsCommentStart
            Dim orgBlockEnd As Boolean = ExDocument.GetCurrentHeaderLineInfo.Real_IsCommentEnd
            Dim orgMaxDisplayLine As Integer = ExDocument.MaxDisplayLineNumber
            Dim orgNextDisplayLineText As String = String.Empty
            If ExWordWrapMode Then
                If ExDocument.DisplayPosition.Y <> ExDocument.MaxDisplayLineNumber Then
                    '获取下一个可见行的信息
                    orgNextDisplayLineText = ExDocument.GetMemoryLineByDisplayLineNumber(ExDocument.DisplayPosition.Y + 1).Text
                End If
            End If
            '当前按键信息
            Dim strChar As String = KeyChar
            'Tab替换
            If KeyChar = Chr(9) Then
                If TabSpace > 0 Then strChar = Space(TabSpace)
            End If
            If ExDocument.HasSelection Then
                '如果原本有选择部分，则用当前输入内容替换选择部分
                ExDocument.UpdateSelectionText(KeyChar)
            Else
                If Me.mInputMode = EnumInputMode.InsertChar Then
                    ExDocument.InsertString(strChar)
                Else
                    ExDocument.UpdateChar(strChar)
                    mCaret.ChangeWidth(GetCaretWidth)
                End If
            End If
            '输入任何文字以后，将取消选中区域
            If ExDocument.HasSelection Then ExDocument.ClearSelection()
            ResetCaretPos()
            '重新计算文档的宽度
            If ExDocument.ResetMaxLineWidth(ExDocument.InputPosition.Y) Then
                RaiseEvent ChangeHScroll()
            End If
            MakeCaretVisible()
            '为了防止光标的残影，先隐藏光标，再绘制文字，最后显示光标
            mCaret.HideCaret()
            '是否注释块标志被改动了
            With ExDocument.ExRealLineManager.GetInfoLine(ExDocument.GetCurrentDisplayLine.Dis_RealLineNumber)
                If Not (orgBlockStart = .Real_IsCommentStart And orgBlockEnd = .Real_IsCommentEnd) Then
                    IsNeedRefresh = True
                Else
                    If ExWordWrapMode Then
                        '折行显示
                        If orgMaxDisplayLine <> ExDocument.MaxDisplayLineNumber Then
                            '如果最大表示行数变化了，重新绘制界面
                            IsNeedRefresh = True
                        Else
                            '一个可见行的文本发生变化的时候，重新绘制界面
                            If orgNextDisplayLineText <> ExDocument.GetMemoryLineByDisplayLineNumber(ExDocument.DisplayPosition.Y + 1).Text Then
                                IsNeedRefresh = True
                            Else
                                '安全起见，重绘中心行
                                DrawLinesByCenterLineNumber(ExDocument.DisplayPosition.Y)
                            End If
                        End If
                    Else
                        DrawLine(ExDocument.DisplayPosition.Y)
                        End If
                End If
            End With
            mCaret.SetCaretPos(CaretPosition)
            '这里必然光标可见
            mCaret.ShowCaret()
            IntellisenseBox.Visible = False
            If KeyChar <> Space(1) Then
                TestAutoComplete()
            End If
            If ExColumnMode Or IsNeedRefresh Then Me.Refresh()
            RaiseEvent InputPositionChanged()
        End If
    End Sub
    ''' <summary>
    ''' 获得键盘按下事件
    ''' </summary>
    ''' <param name="sender"></param>
    ''' <param name="e"></param>
    ''' <remarks></remarks>
    Private Sub TextBoxEx_PreviewKeyDown(ByVal sender As Object, ByVal e As System.Windows.Forms.PreviewKeyDownEventArgs) Handles Me.PreviewKeyDown
        '先假设不能绘制，如果没有被过滤掉的话，则能绘制
        IsDrawString = False
        Dim orgPosition As Point = ExDocument.InputPosition
        Select Case e.KeyCode
            '********************************************************************
            '           开发用功能键
            '********************************************************************
            Case Keys.F7
                ExDocument.StepList.DumpStep()
                '********************************************************************
            Case Keys.Tab
                If Not ExReadOnlyMode Then SimeKey(Chr(9))
            Case Keys.Enter
                If Not (ExReadOnlyMode Or ExColumnMode) Then Key_Enter()
            Case Keys.Back, Keys.Delete
                If Not ExReadOnlyMode Then Key_Del_BS(e)
            Case Keys.Up
                If Not ExColumnMode Then Key_Up(orgPosition, e)
            Case Keys.Down
                Key_Down(orgPosition, e)
            Case Keys.Left
                If Not ExColumnMode Then Key_Left(orgPosition, e)
            Case Keys.Right
                If Not ExColumnMode Then Key_Right(orgPosition, e)
            Case Keys.Insert
                Key_Insert()
            Case Keys.Home
                If Not ExColumnMode Then Key_Home(orgPosition, e)
            Case Keys.End
                If Not ExColumnMode Then Key_End(orgPosition, e)
            Case Keys.PageUp
                If Not ExColumnMode Then Key_PageUp(orgPosition, e)
            Case Keys.PageDown
                If Not ExColumnMode Then Key_PageDown(orgPosition, e)
            Case Else
                '如果没有按下Control的普通键的话，则将输入信息绘制到控件中
                If e.Control = False Then IsDrawString = True
        End Select
        RaiseEvent InputPositionChanged()
    End Sub
#Region "具体按键"
    Sub Key_Enter()
        ExDocument.InsertEnter()
        '重新计算插入位置和光标位置
        ResetCaretPos()
        If mInputMode = EnumInputMode.OverwriteChar Then mCaret.ChangeWidth(GetCaretWidth)
        If ExDocument.ResetMaxLineWidth() Then
            RaiseEvent ChangeHScroll()
        End If
        '智能提示框无条件关闭
        Me.IntellisenseBox.Visible = False
        ResetCaretPos()
        MakeCaretVisible()
        mCaret.SetCaretPos(CaretPosition)
        Me.Refresh()
    End Sub
    Sub Key_Insert()
        If mInputMode = EnumInputMode.InsertChar Then
            mInputMode = EnumInputMode.OverwriteChar
            mCaret.ChangeWidth(GetCaretWidth)
            mCaret.SetCaretPos(CaretPosition)
        Else
            mInputMode = EnumInputMode.InsertChar
            mCaret.ChangeWidth(1)
        End If
        RaiseEvent InputModeChanged(mInputMode)
    End Sub
    Sub Key_Del_BS(ByVal e As PreviewKeyDownEventArgs)
        Dim orgBlockStart As Boolean = ExDocument.ExRealLineManager.GetInfoLine(ExDocument.GetCurrentDisplayLine.Dis_RealLineNumber).Real_IsCommentStart
        Dim orgBlockEnd As Boolean = ExDocument.ExRealLineManager.GetInfoLine(ExDocument.GetCurrentDisplayLine.Dis_RealLineNumber).Real_IsCommentEnd
        Dim IsLineChanged As Boolean
        If ExDocument.HasSelection Then
            Me.Delete()
            Me.Refresh()
        Else
            IsLineChanged = ExDocument.DeleteChar(IIf(e.KeyCode = Keys.Delete, True, False))
            If IsLineChanged Then
                If ExDocument.ResetMaxLineWidth() Then
                    RaiseEvent ChangeHScroll()
                End If
            Else
                If ExDocument.ResetMaxLineWidth(ExDocument.InputPosition.Y) Then
                    RaiseEvent ChangeHScroll()
                End If
            End If
            ResetCaretPos()
            MakeCaretVisible()
            If mInputMode = EnumInputMode.OverwriteChar Then mCaret.ChangeWidth(GetCaretWidth)
            mCaret.SetCaretPos(CaretPosition)
            If IsLineChanged Then
                Me.Refresh()
            Else
                '是否注释块标志被改动了
                With ExDocument.ExRealLineManager.GetInfoLine(ExDocument.GetCurrentDisplayLine.Dis_RealLineNumber)
                    If Not (orgBlockStart = .Real_IsCommentStart And orgBlockEnd = .Real_IsCommentEnd) Then
                        Me.Refresh()
                    Else
                        '为了防止光标的残影，先隐藏光标
                        mCaret.HideCaret()
                        DrawLine(ExDocument.DisplayPosition.Y)
                        '为了防止光标的残影，再绘制光标
                        mCaret.ShowCaret()
                    End If
                End With
            End If
            IntellisenseBox.Visible = False
            TestAutoComplete()
        End If
    End Sub
    '光标操作
    Sub Key_Home(ByVal orgPosition As Point, ByVal e As PreviewKeyDownEventArgs)
        ExDocument.DisplayPosition = New Point(1, ExDocument.DisplayPosition.Y)
        AfterProcess(orgPosition, e)
    End Sub
    Sub Key_End(ByVal orgPosition As Point, ByVal e As PreviewKeyDownEventArgs)
        ExDocument.DisplayPosition = New Point(ExDocument.CurrentLineText.Length + 1, ExDocument.DisplayPosition.Y)
        AfterProcess(orgPosition, e)
    End Sub
    Sub Key_PageUp(ByVal orgPosition As Point, ByVal e As PreviewKeyDownEventArgs)
        ExDocument.DisplayPosition = New Point(1, Max(FirstVisiableLineNumber - VisibleLines, 1))
        JumpToLine(ExDocument.DisplayPosition.Y, VPosition.Top)
        AfterProcess(orgPosition, e)
    End Sub
    Sub Key_PageDown(ByVal orgPosition As Point, ByVal e As PreviewKeyDownEventArgs)
        ExDocument.DisplayPosition = New Point(1, Min(FirstVisiableLineNumber + VisibleLines, ExDocument.MaxDisplayLineNumber))
        JumpToLine(ExDocument.DisplayPosition.Y, VPosition.Top)
        AfterProcess(orgPosition, e)
    End Sub
    Sub Key_Up(ByVal orgPosition As Point, ByVal e As System.Windows.Forms.PreviewKeyDownEventArgs)
        If ExDocument.DisplayPosition.Y > 1 Then
            '向上的箭头，光标位置移动到上一行
            ExDocument.DisplayPosition = GetUpOrDownPoint(True)
            CaretPosition.X = MeasureTextWidth(Mid(ExDocument.GetMemoryLineByDisplayLineNumber(ExDocument.DisplayPosition.Y).Text, 1, ExDocument.DisplayPosition.X - 1)) - offset
            If ExDocument.DisplayPosition.Y < FirstVisiableLineNumber Then
                FirstVisiableLineNumber -= 1
                FirstLineChanged()
            End If
        Else
            '光标到行首
            ExDocument.DisplayPosition = New Point(1, 1)
        End If
        AfterProcess(orgPosition, e)
    End Sub
    Sub Key_Down(ByVal orgPosition As Point, ByVal e As PreviewKeyDownEventArgs)
        '激活候补框
        If IntellisenseBox.Visible Then
            IntellisenseBox.Focus()
            IntellisenseBox.SelectedIndex = 0
            Exit Sub
        End If
        If ExDocument.DisplayPosition.Y < ExDocument.MaxDisplayLineNumber Then
            '向下的箭头，光标位置移动到下一行
            ExDocument.DisplayPosition = GetUpOrDownPoint(False)
            '通过向下箭头到下一行
            If ExDocument.DisplayPosition.Y > Math.Min(CInt(FirstVisiableLineNumber + VisibleLines - 1), CInt(ExDocument.MaxDisplayLineNumber)) Then
                FirstVisiableLineNumber += 1
                FirstLineChanged()
            End If
        Else
            '光标到最后
            ExDocument.DisplayPosition = New Point(ExDocument.CurrentLineText.Length + 1, ExDocument.DisplayPosition.Y)
        End If
        AfterProcess(orgPosition, e)
    End Sub
    Sub Key_Left(ByVal orgPosition As Point, ByVal e As PreviewKeyDownEventArgs)
        '向左的箭头，光标位置移动到前一个字符
        If ExDocument.DisplayPosition.X > 1 Then
            ExDocument.DisplayPosition = New Point(ExDocument.DisplayPosition.X - 1, ExDocument.DisplayPosition.Y)
        Else
            '上一行的尾部
            If ExDocument.DisplayPosition.Y > 1 Then
                '如果行大于1的话，则可以移到尾部
                ExDocument.DisplayPosition = New Point(ExDocument.GetMemoryLineByDisplayLineNumber(ExDocument.DisplayPosition.Y - 1).Text.Length + 1, _
                                                    ExDocument.DisplayPosition.Y - 1)
                If ExDocument.DisplayPosition.Y < FirstVisiableLineNumber Then
                    '如果选中行为非表示行，则表示这行
                    FirstVisiableLineNumber = ExDocument.DisplayPosition.Y
                    '这个时候，自动重新绘制了
                    FirstLineChanged()
                End If
            End If
        End If
        AfterProcess(orgPosition, e)
    End Sub
    Sub Key_Right(ByVal orgPosition As Point, ByVal e As PreviewKeyDownEventArgs)
        '向右的箭头，光标位置移动到后一个字符：全角半角，麻烦的问题
        If ExDocument.DisplayPosition.X - 1 < ExDocument.CurrentLineText.Length Then
            ExDocument.DisplayPosition = New Point(ExDocument.DisplayPosition.X + 1, ExDocument.DisplayPosition.Y)
        Else
            '下一行的行首
            If ExDocument.DisplayPosition.Y < ExDocument.MaxDisplayLineNumber Then
                '如果不是最后一行的话
                ExDocument.DisplayPosition = New Point(1, ExDocument.DisplayPosition.Y + 1)
                If ExDocument.DisplayPosition.Y > FirstVisiableLineNumber + VisibleLines - 1 Then
                    '如果选中行为非表示行，则表示这行
                    FirstVisiableLineNumber += 1
                    '这个时候，自动重新绘制了
                    FirstLineChanged()
                End If
            End If
        End If
        AfterProcess(orgPosition, e)
    End Sub
    ''' <summary>
    ''' 部分按键后的共通操作
    ''' </summary>
    ''' <param name="orgPosition"></param>
    ''' <param name="e"></param>
    ''' <remarks></remarks>
    Sub AfterProcess(ByVal orgPosition As Point, ByVal e As PreviewKeyDownEventArgs)
        If Not (e.Shift Or ExDocument.HasSelection) Then
            If mInputMode = EnumInputMode.OverwriteChar Then
                mCaret.ChangeWidth(GetCaretWidth)
            End If
        End If
        '关闭智能提示
        IntellisenseBox.Visible = False
        '为了防止光标的残影，先隐藏光标
        mCaret.HideCaret()
        ResetCaretPos()
        MakeCaretVisible()
        mCaret.SetCaretPos(CaretPosition)
        '如果是选择模式，则重新绘图
        If e.Shift = True Then
            '设定选中区域
            ExDocument.UpdateSelection(orgPosition)
            '需要重新绘制
            DrawLinesByCenterLineNumber(ExDocument.DisplayPosition.Y)
        Else
            If ExDocument.HasSelection Then
                ExDocument.ClearSelection()
                '需要重新绘制
                Me.Refresh()
            End If
        End If
        '为了防止光标的残影
        mCaret.ShowCaret()
    End Sub
#End Region
#End Region
#Region "鼠标事件"
    ''' <summary>
    ''' 当鼠标移动时候，产生什么效果
    ''' </summary>
    ''' <remarks></remarks>
    Enum MouseMoveEffect
        ''' <summary>
        ''' 无效果
        ''' </summary>
        ''' <remarks></remarks>
        None
        ''' <summary>
        ''' 选择文本
        ''' </summary>
        ''' <remarks></remarks>
        SelectText
        ''' <summary>
        ''' 箱型选择
        ''' </summary>
        ''' <remarks></remarks>
        BoxSelectText
        ''' <summary>
        ''' 用拖曳的方法移动文本
        ''' </summary>
        ''' <remarks></remarks>
        DragToMove
        ''' <summary>
        ''' 设置列模式的列起始点
        ''' </summary>
        ''' <remarks></remarks>
        ColumnSet
    End Enum
    Dim MouseEff As MouseMoveEffect = MouseMoveEffect.None
    '箱型选择的左边距
    Dim BoxSelectLeft As Integer
    '箱型选择的右边距
    Dim BoxSelectRight As Integer
    '列模式起始行
    Dim ColumnModeStartLine As Integer
    '列模式结束行
    Dim ColumnModeEndLine As Integer
    '列模式左边据
    Dim ColumnModeLeft As Integer
    '拖曳事件
    Dim ReadyToDrag As Boolean
    ''' <summary>
    ''' 鼠标松开事件
    ''' </summary>
    ''' <param name="sender"></param>
    ''' <param name="e"></param>
    ''' <remarks></remarks>
    Private Sub TextBoxEx_MouseUp(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles Me.MouseUp
        Select Case MouseEff
            Case MouseMoveEffect.DragToMove
                ExDocument.ClearSelection()
            Case MouseMoveEffect.ColumnSet
                ExDocument.SetColumn(ColumnModeStartLine, ColumnModeEndLine, ColumnModeLeft)
                RaiseEvent ColumnModeEnd(ExDocument.InputPosition.Y)
            Case MouseMoveEffect.SelectText
                With ExDocument
                    If .ExRealLineManager.GetInfoLine(.InputPosition.Y).Real_IsBlockHeader And _
                       .ExRealLineManager.GetInfoLine(.InputPosition.Y).Real_IsFolder Then
                        .UpdateSelection(.InputPosition)
                    End If
                End With
        End Select
        Me.Refresh()
    End Sub
    ''' <summary>
    ''' 鼠标按下事件
    ''' </summary>
    ''' <param name="sender"></param>
    ''' <param name="e"></param>
    ''' <remarks></remarks>
    Private Sub TextBoxEx_MouseDown(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles Me.MouseDown
        BoxSelectLeft = 0
        BoxSelectRight = 0
        ReadyToDrag = False
        Dim orgPos As Point = ExDocument.InputPosition
        SetInputPosByMouse(New Point(e.X, e.Y))
        If e.Button = Windows.Forms.MouseButtons.Left Then
            If Control.ModifierKeys And Keys.Shift = Control.ModifierKeys Then
                'Shift键
                ExDocument.UpdateSelection(orgPos)
            Else
                If ExDocument.HasSelection Then
                    '如果当前插入点位于选中文本,并且是普通选中模式
                    If (ComparePoint(ExDocument.InputPosition, ExDocument.SelectStartRealPos) = CompareResult.Equal Or _
                        ComparePoint(ExDocument.InputPosition, ExDocument.SelectEndRealPos) = CompareResult.Equal And _
                        ExDocument.SelectEndRealPos.X - ExDocument.SelectStartRealPos.X = 1) Or _
                       (ComparePoint(ExDocument.InputPosition, ExDocument.SelectStartRealPos) = CompareResult.Big And _
                        ComparePoint(ExDocument.InputPosition, ExDocument.SelectEndRealPos) = CompareResult.Small And _
                        Not (ExDocument.InputPosition.X = ExDocument.CurrentLineText.Length + 1)) And _
                       (ExDocument.CurrentSelectType = Document.SelectType.Normal) Then
                        MouseEff = MouseMoveEffect.DragToMove
                        ReadyToDrag = True
                    Else
                        ExDocument.ClearSelection()
                    End If
                End If
                If ReadyToDrag = False Then
                    If ExColumnMode Or ExBoxSelectMode Then
                        If ExColumnMode Then
                            '列模式
                            MouseEff = MouseMoveEffect.ColumnSet
                            ColumnModeStartLine = ExDocument.InputPosition.Y
                            ColumnModeEndLine = ExDocument.InputPosition.Y
                            ColumnModeLeft = MeasureTextWidth(Mid(ExDocument.CurrentLineText, 1, ExDocument.InputPosition.X - 1)) + offset
                            ExDocument.SetColumn(ColumnModeStartLine, ColumnModeEndLine, ColumnModeLeft)
                            RaiseEvent ColumnModeStart(ColumnModeStartLine)
                        End If
                        If ExBoxSelectMode Then
                            '箱型选择
                            MouseEff = MouseMoveEffect.BoxSelectText
                            BoxSelectLeft = e.X + offset
                        End If
                    Else
                        '清除列模式数据
                        RaiseEvent ClearColumn()
                        ExDocument.ColumnModeList.Clear()
                        ExDocument.FirstColumnLine = 0
                        ExDocument.LastColumnLine = 0
                        ColumnModeLeft = 0
                        '普通的选择
                        MouseEff = MouseMoveEffect.SelectText
                    End If
                End If
            End If
            If mInputMode = EnumInputMode.OverwriteChar Then
                mCaret.ChangeWidth(GetCaretWidth)
            End If
            Me.Refresh()
        End If
    End Sub
    ''' <summary>
    ''' 鼠标移动事件
    ''' </summary>
    ''' <param name="sender"></param>
    ''' <param name="e"></param>
    ''' <remarks></remarks>
    Private Sub TextBoxEx_MouseMove(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles Me.MouseMove
        '原始的插入点
        Dim orgPos As Point = ExDocument.InputPosition
        '是否需要重新绘制
        Dim IsNeedRefresh As Boolean = False
        If ReadyToDrag Then
            ReadyToDrag = False
            Me.DoDragDrop(String.Empty, DragDropEffects.Move)
            Exit Sub
        End If
        If e.Button = Windows.Forms.MouseButtons.None Then
            '如果鼠标没有按键被按下，则检查ToolTip是否更新
            Dim OldWord As SingleWord = CurrentMouseWord
            ResetMouseWord(Me.PointToClient(Control.MousePosition).X, Me.PointToClient(Control.MousePosition).Y)
            If CurrentMouseWord.Pos.Y = 0 Then Exit Sub
            With ExDocument.GetMemoryLineByDisplayLineNumber(CurrentMouseWord.Pos.Y)
                If .Real_IsBlockHeader And .Real_IsFolder Then
                    '如果是折叠行首的话
                    If OldWord.Pos.Y <> CurrentMouseWord.Pos.Y Then
                        Me.mToolTip.SetToolTip(Me, ExDocument.GetBlockText(.Dis_RealLineNumber))
                    End If
                Else
                    '普通代码行
                    If Not ComparePoint(OldWord.Pos, CurrentMouseWord.Pos) = CompareResult.Equal Then
                        '位置变动了
                        'Debug.WriteLine(UCase(CurrentMouseWord.StrWord) & " " & .Dis_RealLineNumber)
                        mToolTip.RemoveAll()
                        If tblToolTip.ContainsKey(UCase(CurrentMouseWord.StrWord)) Then
                            Me.mToolTip.SetToolTip(Me, CurrentMouseWord.StrWord & vbCrLf & tblToolTip.Item(UCase(CurrentMouseWord.StrWord)))
                        End If
                    End If
                End If
            End With
        End If
        '************************************************************
        '       鼠标选中文本  开始
        '************************************************************
        If e.Button = Windows.Forms.MouseButtons.Left Then
            '如果左键被按下，现在处于选择文本模式
            If e.Y <= 0 Then
                '如果鼠标位于可见区域上方，向上滚动一行
                If ExDocument.InputPosition.Y > 1 Then
                    '向上的箭头，光标位置移动到上一行
                    ExDocument.InputPosition = GetUpOrDownPoint(True)
                    CaretPosition.X = MeasureTextWidth(Mid(ExDocument.GetMemoryLine(ExDocument.InputPosition.Y).Text, 1, ExDocument.InputPosition.X - 1))
                    If ExDocument.InputPosition.Y < FirstVisiableLineNumber Then
                        FirstVisiableLineNumber -= 1
                        FirstLineChanged()
                    Else
                        CaretPosition.Y -= ExFont.Height
                    End If
                End If
            Else
                If e.Y > ExFont.Height * VisibleLines Then
                    '如果鼠标位于可见区域下方，向下滚动一行
                    If ExDocument.InputPosition.Y <> ExDocument.MaxMemoryLineNumber Then
                        '向下的箭头，光标位置移动到下一行
                        ExDocument.InputPosition = GetUpOrDownPoint(False)
                        CaretPosition.X = MeasureTextWidth(Mid(ExDocument.GetMemoryLine(ExDocument.InputPosition.Y).Text, 1, ExDocument.InputPosition.X - 1))
                        '通过向下箭头到下一行
                        If ExDocument.InputPosition.Y > Math.Min(CInt(FirstVisiableLineNumber + VisibleLines - 1), CInt(ExDocument.MaxMemoryLineNumber)) Then
                            FirstVisiableLineNumber += 1
                            FirstLineChanged()
                        Else
                            CaretPosition.Y += ExFont.Height
                        End If
                    End If
                End If
            End If
            '不然的话，重新设置插入点。如果插入点没有改变位置，则退出过程
            SetInputPosByMouse(New Point(e.X, e.Y))
            Select Case MouseEff
                Case MouseMoveEffect.SelectText
                    '选择文本
                    If Not ComparePoint(orgPos, ExDocument.InputPosition) = CompareResult.Equal Then
                        ExDocument.UpdateSelection(orgPos)
                    End If
                Case MouseMoveEffect.BoxSelectText
                    '箱型选择
                    BoxSelectRight = e.X + offset
                    ExDocument.UpdateBoxSelection(orgPos, BoxSelectLeft, BoxSelectRight)
                Case MouseMoveEffect.ColumnSet
                    '列模式
                    RaiseEvent ColumnModeEnd(ExDocument.InputPosition.Y)
                    ColumnModeEndLine = ExDocument.InputPosition.Y
                    ExDocument.SetColumn(ColumnModeStartLine, ColumnModeEndLine, ColumnModeLeft)
                Case MouseMoveEffect.DragToMove
                    '代码段拖曳
                Case MouseMoveEffect.None
            End Select
            IsNeedRefresh = True

            '对于水平方向的修正
            If e.X <= 0 Then
                If offset > 0 Then offset -= offsetUnit
                RaiseEvent ChangeHScroll()
                IsNeedRefresh = True
            End If
            If e.X > Me.Width Then
                If offset + Width < ExDocument.MaxWidth Then offset += offsetUnit
                RaiseEvent ChangeHScroll()
                IsNeedRefresh = True
            End If
        End If
        '************************************************************
        '       鼠标选中文本  结束
        '************************************************************
        If IsNeedRefresh Then Me.Refresh()
    End Sub
    ''' <summary>
    ''' 鼠标滚轮事件
    ''' </summary>
    ''' <param name="sender"></param>
    ''' <param name="e"></param>
    ''' <remarks></remarks>
    Private Sub TextBoxEx_MouseWheel(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles Me.MouseWheel
        '用鼠标滚轮开改变字体
        If Control.ModifierKeys And Keys.Control = Control.ModifierKeys Then
            If e.Delta > 0 Then
                ExFont = New Font(ExFont.FontFamily, Min(ExFont.Size + 1, 14))
            Else
                ExFont = New Font(ExFont.FontFamily, Max(9, ExFont.Size - 1))
            End If
            RaiseEvent FontChange(ExFont)
            If ExWordWrapMode Then
                RaiseEvent ReWordWrap()
            End If
            Me.Refresh()
            Exit Sub
        End If
        If IntellisenseBox.Visible Then Exit Sub
        ' 这里的鼠标滚轮是参考了MSDN的MouseWheel的例子
        Dim numberOfTextLinesToMove As Integer = e.Delta * SystemInformation.MouseWheelScrollLines / 120 * -1 ' WHEEL_DATA
        If FirstVisiableLineNumber + numberOfTextLinesToMove > 0 Then
            FirstVisiableLineNumber = FirstVisiableLineNumber + numberOfTextLinesToMove
            If FirstVisiableLineNumber > ExDocument.MaxDisplayLineNumber Then
                FirstVisiableLineNumber = ExDocument.MaxDisplayLineNumber
            End If
            FirstLineChanged()
        Else
            FirstVisiableLineNumber = 1
            FirstLineChanged()
        End If
        Me.mToolTip.RemoveAll()
    End Sub
    ''' <summary>
    ''' 根据鼠标位置来设定文档输入点位置
    ''' </summary>
    ''' <param name="MousePos">鼠标的位置</param>
    ''' <remarks></remarks>
    Private Sub SetInputPosByMouse(ByVal MousePos As Point)
        Dim OldPosition As Point = ExDocument.InputPosition
        ExDocument.DisplayPosition = GetDisplayPosByMouse(MousePos)
        If MouseEff = MouseMoveEffect.DragToMove Then
            '代码拖曳
            Dim MemoryNo As Integer = ExDocument.ExDisplayManager.GetMemoryNoByDisplayNo(ExDocument.DisplayPosition.Y)
            If MemoryNo > 0 Then
                With ExDocument.MemoryLines(MemoryNo - 1)
                    If .Real_IsBlockHeader And .Real_IsFolder Then
                        '对于代码折叠首行的特殊处理:将代码插入到头部或尾部
                        If ExDocument.DisplayPosition.X < .Text.Length / 2 Then
                            ExDocument.DisplayPosition = New Point(1, ExDocument.DisplayPosition.Y)
                        Else
                            ExDocument.DisplayPosition = New Point(.Text.Length + 1, ExDocument.DisplayPosition.Y)
                        End If
                    End If
                End With
            End If
        End If
        ResetCaretPos()
        If Not ComparePoint(OldPosition, ExDocument.InputPosition) = CompareResult.Equal Then
            mCaret.SetCaretPos(CaretPosition)
            mCaret.ShowCaret()
            '如果是选中模式，但是，这次按键没有Shfit的话，则清除选中区域
            If (Control.ModifierKeys And Keys.ShiftKey <> Keys.ShiftKey) And ExDocument.HasSelection Then
                ExDocument.ClearSelection()
                Me.Refresh()
            End If
            RaiseEvent InputPositionChanged()
        End If
    End Sub
    ''' <summary>
    ''' 鼠标双击事件
    ''' </summary>
    ''' <param name="sender"></param>
    ''' <param name="e"></param>
    ''' <remarks></remarks>
    Private Sub TextBoxEx_MouseDoubleClick(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles Me.MouseDoubleClick
        ResetMouseWord(e.X, e.Y)
        If CurrentMouseWord.StrWord <> "" Then
            ExDocument.ClearSelection()
            ExDocument.DisplayPosition = New Point(CurrentMouseWord.Pos.X + CurrentMouseWord.Length, CurrentMouseWord.Pos.Y)
            '如果双击的情况，光标变为插入状态，这时和编辑状态无关
            mCaret.ChangeWidth(1)
            '重新设置光标位置
            ResetCaretPos()
            '这里把词首位置设置为原来插入点
            ExDocument.UpdateSelection(ExDocument.GetInputPosByDisplayPos(CurrentMouseWord.Pos))
            Me.Refresh()
        End If
    End Sub
    ''' <summary>
    ''' 重置鼠标位置的单词
    ''' </summary>
    ''' <param name="X"></param>
    ''' <param name="Y"></param>
    ''' <remarks></remarks>
    Private Sub ResetMouseWord(ByVal X As Integer, ByVal Y As Integer)
        CurrentMouseWord.StrWord = ""
        CurrentMouseWord.Pos = New Point(0, 0)
        CurrentMouseWord.Length = 0
        If Math.Ceiling(Y / ExFont.Height) <= 0 Then Exit Sub
        If Math.Ceiling(Y / ExFont.Height) <= ExDocument.MaxDisplayLineNumber - FirstVisiableLineNumber + 1 Then
            Dim TempY As Integer
            TempY = Math.Ceiling(Y / ExFont.Height) + FirstVisiableLineNumber - 1
            Dim CurrentLine As Line = ExDocument.GetMemoryLineByDisplayLineNumber(TempY)
            If CurrentLine.WordList.Count > 0 Then
                For i As Integer = 0 To CurrentLine.WordList.Count - 1
                    Dim StartPos As Integer
                    Dim EndPos As Integer
                    If CurrentLine.WordList(i).StartPos - 1 = 0 Then
                        StartPos = 0
                    Else
                        StartPos = MeasureTextWidth(Mid(CurrentLine.Text, 1, CurrentLine.WordList(i).StartPos - 1)) - offset
                    End If
                    EndPos = MeasureTextWidth(Mid(CurrentLine.Text, 1, CurrentLine.WordList(i).StartPos + CurrentLine.WordList(i).Length - 1)) - offset
                    If (X < EndPos) And (X > StartPos) Then
                        CurrentMouseWord.Pos = New Point(CurrentLine.WordList(i).StartPos, TempY)
                        CurrentMouseWord.StrWord = Mid(CurrentLine.Text, CurrentLine.WordList(i).StartPos, CurrentLine.WordList(i).Length)
                        CurrentMouseWord.Length = CurrentLine.WordList(i).Length
                        Exit For
                    Else
                        CurrentMouseWord.Pos = New Point(0, TempY)
                    End If
                Next
            End If
        End If
    End Sub
    ''' <summary>
    ''' 鼠标拖曳时
    ''' </summary>
    ''' <param name="sender"></param>
    ''' <param name="e"></param>
    ''' <remarks></remarks>
    Private Sub TextBoxEx_DragOver(ByVal sender As Object, ByVal e As System.Windows.Forms.DragEventArgs) Handles Me.DragOver
        e.Effect = DragDropEffects.Move
        Dim MouseE As New MouseEventArgs(Windows.Forms.MouseButtons.Left, 0, Me.PointToClient(New Point(e.X, e.Y)).X, Me.PointToClient(New Point(e.X, e.Y)).Y, 0)
        TextBoxEx_MouseMove(sender, MouseE)
    End Sub
    ''' <summary>
    ''' 鼠标拖曳结束
    ''' </summary>
    ''' <param name="sender"></param>
    ''' <param name="e"></param>
    ''' <remarks></remarks>
    Private Sub TextBoxEx_DragDrop(ByVal sender As Object, ByVal e As System.Windows.Forms.DragEventArgs) Handles Me.DragDrop
        '文件拖曳
        If CType(e.Data, DataObject).GetFileDropList.Count > 0 Then
            If Not ExReadOnlyMode Then RaiseEvent DropFile(CType(e.Data, DataObject).GetFileDropList(0))
            Exit Sub
        End If
        If MouseEff = MouseMoveEffect.DragToMove And Not ExReadOnlyMode Then
            With ExDocument
                '如果拖曳目的地是折叠显示的第一行,必须先打开折叠块,然后把插入点放到折叠段的首部或者尾部
                '代码拖曳
                Dim MemoryNo As Integer = ExDocument.ExDisplayManager.GetMemoryNoByDisplayNo(ExDocument.DisplayPosition.Y)
                If MemoryNo > 0 Then
                    With ExDocument.MemoryLines(MemoryNo - 1)
                        If .Real_IsBlockHeader And .Real_IsFolder Then
                            Dim orgInput As Point = ExDocument.InputPosition
                            '展开代码
                            ExDocument.Expend(ExDocument.DisplayPosition.Y)
                            If ExDocument.DisplayPosition.X = 1 Then
                                '放在代码段头部
                                ExDocument.InputPosition = New Point(1, orgInput.Y)
                            Else
                                '放在代码段尾部
                                Dim EndLine As Integer = ExDocument.GetBlockFooterRealNo(orgInput.Y)
                                ExDocument.InputPosition = New Point(ExDocument.GetRealLineText(EndLine).Length + 1, EndLine)
                            End If
                        End If
                    End With
                End If

                If Not ((ComparePoint(.InputPosition, .SelectStartRealPos) = CompareResult.Big And _
                     ComparePoint(.InputPosition, .SelectEndRealPos) = CompareResult.Small) Or _
                    (ComparePoint(.InputPosition, .SelectStartRealPos) = CompareResult.Equal Or _
                     ComparePoint(.InputPosition, .SelectEndRealPos) = CompareResult.Equal)) Then
                    Dim orgPos As Point = .InputPosition
                    Dim tempText As String = .GetSelectionText
                    '计算由于删除操作，对插入点的影响。由于DeleteSection将自动去除选中文本，所以
                    '必须在DeleteSection前计算。
                    If ComparePoint(.InputPosition, .SelectEndRealPos) = CompareResult.Big Then
                        '如果插入点位于选中区域的前面，则毫无影响
                        If .SelectStartRealPos.Y = .SelectEndRealPos.Y Then
                            '如果删除只是涉及到一行的话
                            If orgPos.Y = .SelectStartRealPos.Y Then
                                '如果删除行和插入行相同的话
                                orgPos = New Point(orgPos.X - tempText.Length, orgPos.Y)
                            End If
                        Else
                            '如果删除涉及到多行的话
                            Dim tempX As Integer = 0
                            Dim tempY As Integer = 0
                            tempY = orgPos.Y - (.SelectEndRealPos.Y - .SelectStartRealPos.Y)
                            If .SelectEndRealPos.Y = orgPos.Y Then
                                '删除部分的末尾和插入点同行的话
                                tempX = orgPos.X - .GetMemoryLine(.SelectEndRealPos.Y).SelectLength
                            Else
                                tempX = orgPos.X
                            End If
                            orgPos = New Point(tempX, tempY)
                        End If
                    End If
                    Dim T1, T2 As StepManager.StruStep
                    With T1
                        .Action = StepManager.StepName.MulitStart
                        .Text = StepManager.StepName.MoveSelection.ToString
                        .NewText = ""
                    End With
                    ExDocument.StepList.Add(T1)
                    .DeleteSelection()
                    .InputPosition = orgPos
                    .InsertSelection(tempText)
                    .UpdateSelection(orgPos, .InputPosition)
                    With T2
                        .Action = StepManager.StepName.MulitEnd
                        .Text = StepManager.StepName.MoveSelection.ToString
                        .NewText = ""
                    End With
                    ExDocument.StepList.Add(T2)
                    ResetCaretPos()
                    mCaret.SetCaretPos(CaretPosition)
                Else
                    ExDocument.ClearSelection()
                End If
            End With
        End If
        Me.Refresh()
        MouseEff = MouseMoveEffect.None
    End Sub
#End Region
End Class
